package tools

import (
	"bufio"
	"crypto/md5"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/qichengzx/coordtransform"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

// FileExist 这里需要判断错误类型, 有可能是权限不足, 无法进入目录不知道目录类型
func FileExist(filePath string) (bool, error) {
	_, err := os.Stat(filePath)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) == true {
		return false, nil
	}
	return false, err
}

func IsDir(path string) (bool, error) {
	// 获取文件或目录的信息
	fileInfo, err := os.Stat(path)
	if err != nil {
		return false, err
	}
	// 判断是否是目录
	if fileInfo.IsDir() {
		return true, nil
	} else {
		return false, nil
	}
}

func Md5(str string) string {
	h := md5.New()
	_, _ = io.WriteString(h, str)
	return fmt.Sprintf("%x", h.Sum(nil))
}

// GetNow 获取当前日期 年月日 时分秒
func GetNow() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

// GetMicro 获取当前微秒级时间戳 =>
func GetMicro() string {
	micro := time.Now().UnixMicro()
	return strconv.FormatInt(micro, 10)
}

// GetNano 获取当前纳秒级时间戳 =>
func GetNano() string {
	nano := time.Now().UnixNano()
	return strconv.FormatInt(nano, 10)
}

// UrlReset 重构url参数
func UrlReset(originUrl string, parameters map[string]string) (dstUrl string, err error) {
	resUrl, err := url.Parse(originUrl)
	if err != nil {
		return
	}
	params := url.Values{}
	for key, val := range parameters {
		params.Set(key, val)
	}
	if resUrl.RawQuery == "" {
		resUrl.RawQuery = params.Encode()
	} else {
		resUrl.RawQuery = resUrl.RawQuery + "&" + params.Encode()
	}
	dstUrl = resUrl.String()
	return
}

// GetRandomString 随机字符串
func GetRandomString(n int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bytes := []byte(str)
	var result []byte
	for i := 0; i < n; i++ {
		result = append(result, bytes[rand.Intn(len(bytes))])
	}
	return string(result)
}

// GetRandomNumberStr 随机数字字符串
func GetRandomNumberStr(n int) string {
	rand.Seed(time.Now().UnixNano())
	str := ""
	for i := 0; i < n; i++ {
		str += strconv.Itoa(rand.Intn(10))
	}
	return str
}

// 截取字符串，支持多字节字符
// start：起始下标，负数从从尾部开始，最后一个为-1
// length：截取长度，负数表示截取到末尾

func SubStr(str string, start int, length int) (result string) {
	s := []rune(str)
	total := len(s) // 全部字数
	if total == 0 {
		return
	}
	// 允许从尾部开始计算
	if start < 0 {
		start = total + start
		if start < 0 {
			return
		}
	}
	if start > total {
		return
	}
	// 到末尾
	end := total
	if length == 0 {
		end = total
	} else if length < 0 {
		end = total + length
	} else {
		end = start + length
	}
	if end <= start {
		return
	}
	if end > total {
		result = string(s[start:])
	} else {
		result = string(s[start:end])
	}
	return
}

// 分隔符拼接数组

func JoinIntArray(arr []int, separator string) string {
	if len(arr) > 0 {
		str := ""
		for _, v := range arr {
			str += fmt.Sprintf("%v%v", v, separator)
		}
		return SubStr(str, 0, -1)
	}
	return ""
}

func JoinStringArray(arr []string, separator string) string {
	if len(arr) > 0 {
		str := ""
		for _, v := range arr {
			str += fmt.Sprintf("%v,", v)
		}
		return SubStr(str, 0, -1)
	}
	return ""
}

type IpLocation struct {
	Status    string `json:"status"`
	Info      string `json:"info"`
	InfoCode  string `json:"infocode"`
	Province  string `json:"province"`
	City      string `json:"city"`
	AdCode    string `json:"adcode"`
	Rectangle string `json:"rectangle"`
}

func GetLocationByIP(ip string) (location *IpLocation, err error) {
	key := "3a69bef99855dc5d7e80809e59111a4b"
	params := url.Values{}
	params.Set("key", key)
	params.Set("ip", ip)
	reqUrl := "https://restapi.amap.com/v3/ip?" + params.Encode()
	fmt.Println("高德地图:  ", reqUrl)
	response, err := http.Get(reqUrl) //发起get请求
	if err != nil {
		return
	}
	defer response.Body.Close()                //关闭请求
	body, err := ioutil.ReadAll(response.Body) //解析请求信息
	if err != nil {
		return
	}
	location = &IpLocation{}
	err = json.Unmarshal(body, location) //转换为map
	if err != nil {
		return
	}
	if location.Status == "0" {
		err = errors.New(location.Info)
		return
	}
	return
}

// ReadFileContent 读取文件内容
func ReadFileContent(filePath string) (contentStr string, err error) {
	contentByte, err := os.ReadFile(filePath)
	if err != nil {
		return
	}
	contentStr = string(contentByte)
	return
}

// GetRandAreaNumber 在范围内取随机数
func GetRandAreaNumber(min int, max int) int {
	return rand.Intn(max-min+1) + min
}

func SignFromMapString(params interface{}, appSecret string) (string, error) {
	byteData, _ := json.Marshal(params)
	var data map[string]string
	err := json.Unmarshal(byteData, &data)
	if err != nil {
		return "", err
	}
	v := url.Values{}
	for key, value := range data {
		v.Set(key, value)
	}
	queryString := v.Encode()
	queryString = strings.ToLower(queryString + "&app_secret=" + appSecret)
	sign := Md5(queryString)
	return sign, nil

}

// CreateDir 创建目录
func CreateDir(dirName string) {
	err := os.MkdirAll(dirName, 0755)
	if err != nil {
		log.Fatal(err)
	}
	//fmt.Println("目录创建成功")
}

/*
GetLngLat

	经纬度格式转换
	input := "3903.9034,N,09710.4206,E"
	res := getLngLat(input)
	fmt.Println("RES: ", res)
*/
func GetLngLat(str string) [2]string {

	re := regexp.MustCompile(`^(\d{1,5}(\.\d{0,4})?),N,(\d{1,5}(\.\d{0,4})?),E$`)

	match := re.FindStringSubmatch(str)

	if len(match) == 5 {
		lat := LngLatFormat(match[1], 2)
		lng := LngLatFormat(match[3], 3)
		if lat != 0 && lng != 0 {
			lng, lat = coordtransform.WGS84toGCJ02(117.137142, 39.077247)
			return [2]string{strconv.FormatFloat(lng, 'f', 6, 64), strconv.FormatFloat(lat, 'f', 6, 64)}
		}
	}
	return [2]string{"", ""}
}

// LngLatFormat midIndex 2 lat 3 lng
func LngLatFormat(str string, midIndex int) float64 {
	re := regexp.MustCompile(`^(\d{1,5}(\.\d{0,4})?)$`)
	match := re.FindStringSubmatch(str)
	if len(match) == 3 {
		f1, err := strconv.ParseFloat(SubStr(str, 0, midIndex), 10)
		if err != nil {
			return 0
		}
		f2, err := strconv.ParseFloat(SubStr(str, midIndex, 15), 10)
		if err != nil {
			return 0
		}
		return f1 + f2/60
	} else {
		return 0
	}
}

func Quit() {
	fmt.Println("按任意键退出...")
	reader := bufio.NewReader(os.Stdin)
	_, _, _ = reader.ReadRune()
	fmt.Println("程序已退出")
}

var logLock sync.Mutex

func LogInfo(cmd string, content string, fileName string) {
	if fileName == "" {
		fileName = "common.log"
	}
	fileName = "./log/" + fileName
	CreateDir(filepath.Dir(fileName))
	logFile, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	if err != nil {
		fmt.Println("os.OpenFile")
		log.Fatal(err)
	}
	defer logFile.Close()
	logLock.Lock()
	_, _ = logFile.Write([]byte("[" + GetNow() + "]" + "\t" + cmd + "\n"))
	_, _ = logFile.Write([]byte(content))
	_, _ = logFile.Write([]byte("\n"))
	logLock.Unlock()
}

// IntSliceToString 逗号拼接数字
func IntSliceToString(nums []int) string {
	strSlice := make([]string, len(nums))
	for i, num := range nums {
		strSlice[i] = strconv.Itoa(num)
	}
	return strings.Join(strSlice, ",")
}

// IsBitSet 判断二进制第几位是否为1
func IsBitSet(n, pos int) bool {
	mask := 1 << pos
	return (n & mask) != 0
}

// HexToInt64 16进制转10进制
func HexToInt64(hexStr string) (int64, error) {
	decimal, err := strconv.ParseInt(hexStr, 16, 64)
	if err != nil {
		return 0, err
	}
	return decimal, nil
}

// FromUnixTime 时间戳(秒级)转字符串日期
func FromUnixTime(ts int64) string {
	return time.Unix(ts, 0).Format("2006-01-02 15:04:05")
}

// StrToTimeStamp 字符格式转时间戳, example: 2023-12, 2023-12-09, 2023-12-09 13:26:00
func StrToTimeStamp(str string) int64 {
	if len(str) == 10 {
		str = str + " 00:00:00"
	}
	if len(str) == 7 {
		str = str + "-01 00:00:00"
	}
	t, err := time.ParseInLocation("2006-01-02 15:04:05", str, time.Local)
	if err != nil {
		return 0
	}
	// 使用 time.Unix() 函数将时间对象转换为时间戳
	timestamp := t.Unix()
	return timestamp
}

// StrToTime 字符格式转时间, example: 2023-12, 2023-12-09, 2023-12-09 13:26:00
func StrToTime(str string) time.Time {
	if len(str) == 10 {
		str = str + " 00:00:00"
	}
	if len(str) == 7 {
		str = str + "-01 00:00:00"
	}
	t, err := time.ParseInLocation("2006-01-02 15:04:05", str, time.Local)
	if err != nil {
		return time.Time{}
	}
	return t
}

// LastDayInMonth 获取某个月的最后一天最后一秒的时间格式
func LastDayInMonth(str string) time.Time {
	today := StrToTime(str)
	// 获取下一个月的第一天
	nextMonth := today.AddDate(0, 1, 0)
	firstDayOfNextMonth := time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, nextMonth.Location())
	// 获取本月最后一天
	lastDayOfThisMonth := firstDayOfNextMonth.Add(-time.Second)
	return lastDayOfThisMonth
}

// GetRangeTime 获取某日期的起始时间
func GetRangeTime(date string) [2]time.Time {
	t := StrToTime(date)
	if t.Unix() < 0 {
		t = time.Now()
	}
	location, _ := time.LoadLocation("UTC")
	t = t.In(location)
	startOfDay := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, location)
	endOfDay := startOfDay.Add((3600*24 - 1) * time.Second)
	return [2]time.Time{
		startOfDay,
		endOfDay,
	}
}

// ValidatePhone 验证手机号
func ValidatePhone(phone string) bool {
	// 使用正则表达式检查手机号的格式
	reg := regexp.MustCompile(`^1[3456789]\d{9}$`)
	return reg.MatchString(phone)
}

// Filter 切片过滤 返回符合条件的新切片
func Filter[T any](slice []T, condition func(T) bool) []T {
	var filtered []T
	for _, item := range slice {
		if condition(item) {
			filtered = append(filtered, item)
		}
	}
	return filtered
}

// SliceInsert 切片指定位置插入元素
func SliceInsert[T any](a []T, deleteIndex int, b ...T) ([]T, error) {
	var res []T
	if deleteIndex < 0 || deleteIndex > len(a) {
		return a, errors.New("deleteIndex exceeded")
	}
	// 插入元素
	left := a[0:deleteIndex]
	right := a[deleteIndex:]
	res = make([]T, 0, len(a)+len(b))
	b = append(b, right...) // 注意,不可以这样顺序 append(left,b,right)
	res = append(left, b...)
	return res, nil
}

// SliceDelete 切片指定位置删除元素
func SliceDelete[T any](a []T, deleteIndex int) ([]T, error) {
	var res []T
	if deleteIndex < 0 || deleteIndex >= len(a) {
		return a, errors.New("deleteIndex exceeded")
	}
	// 删除元素
	left := a[0:deleteIndex]
	right := a[(deleteIndex + 1):]
	res = make([]T, 0, len(a)-1)
	res = append(left, right...)
	return res, nil
}

// SliceReverse 切片倒置
func SliceReverse[T any](a *[]T) {
	length := len(*a)
	for i := 0; i < length/2; i++ {
		(*a)[i], (*a)[length-i-1] = (*a)[length-i-1], (*a)[i]
	}
}

// StrPad 字符串补位
func StrPad(str, pad, pos string, n int) string {
	l := len(str)
	if l >= n {
		return str
	}
	padStr := ""
	for i := 0; i < n-l; i++ {
		padStr += pad
	}

	if pos == "left" {
		str = padStr + str
	}
	if pos == "right" {
		str = str + padStr
	}
	return str
}

// GetExcelCols 获取excel列 A ~ AZ
func GetExcelCols() []string {
	return []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN", "AO", "AP", "AQ", "AR", "AS", "AT", "AU", "AV", "AW", "AX", "AY", "AZ"}
}

func MaxInt(slice []int) (value, index int) {
	value = 0
	index = -1
	if len(slice) == 0 {
		return
	}
	value = slice[0]
	index = 0
	for k, item := range slice {
		if item > value {
			value = item
			index = k
		}
	}
	return
}

func MinInt(slice []int) (value, index int) {
	value = 0
	index = -1
	if len(slice) == 0 {
		return
	}
	value = slice[0]
	index = 0
	for k, item := range slice {
		if item < value {
			value = item
			index = k
		}
	}
	return
}

func StrToIntSlice(str string) []int {
	a := make([]int, 0)
	s := strings.Split(str, ",")
	for _, v := range s {
		if v != "" {
			n, _ := strconv.Atoi(v)
			a = append(a, n)
		}
	}
	return a
}
